// ==UserScript==
// @name         123 云盘会员青春版
// @author       hmjz100、Qing
// @namespace    github.com/hmjz100
// @version      1.0.6.2
// @description  《也许同类型中最好用？》系列、LinkSwift 官方扩展 - 模拟 123 云盘会员，可超 1GB 上限下载；自带配置 UI，无需再修改脚本；同时适配新旧版分享页，还支持使用视频倍速、隐藏广告、修改头像、修改用户名、修改等级等功能，自定义程度超高！
// @icon         https://gitee.com/selling-surprises/Greasyfork-Script-repository/raw/main/%E7%BD%91%E7%9B%98/123%E7%BD%91%E7%9B%98/123pan.ico
// @license      AGPL-3.0-or-later
// @homepage     https://github.com/hmjz100/123panYouthMember/
// @homepageURL  https://github.com/hmjz100/123panYouthMember/
// @support      https://github.com/hmjz100/123panYouthMember/issues
// @supportURL   https://github.com/hmjz100/123panYouthMember/issues
// @require      https://unpkg.com/jquery@3.6.0/dist/jquery.min.js
// @require      https://unpkg.com/sweetalert2@11.4.8/dist/sweetalert2.min.js
// @run-at       document-start
// @early-start
// @match        *://*.123pan.com/*
// @match        *://*.123pan.cn/*
// @match        *://*.123684.com/*
// @match        *://*.123865.com/*
// @match        *://*.123952.com/*
// @match        *://*.123912.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        unsafeWindow
// @compatible	 Chrome
// @compatible	 Edge
// @compatible	 Firefox
// @compatible	 Safari
// @compatible	 Opera
// @downloadURL https://update.greasyfork.dpdns.org/scripts/513528/123%20%E4%BA%91%E7%9B%98%E4%BC%9A%E5%91%98%E9%9D%92%E6%98%A5%E7%89%88.user.js
// @updateURL https://update.greasyfork.dpdns.org/scripts/513528/123%20%E4%BA%91%E7%9B%98%E4%BC%9A%E5%91%98%E9%9D%92%E6%98%A5%E7%89%88.meta.js
// ==/UserScript==
/*
项目并未以任何形式承诺、宣传或实现对网盘会员功能的破解，亦未声称具备 “完美解锁会员功能” 等能力。
此项目部分功能（如下载超过 1GB 的文件）仅为模拟移动端权限，该权限属官方允许范围且可能随时调整。
请勿将其误解为本项目具备破解的功能或提供相应服务。
任何第三方修改版本若因夸大宣传或混淆概念导致的问题，均与本脚本无关。
*/
/*
项目仅使用 AI 辅助编码，严格杜绝 Vibe Coding；
代码均经过人工复验，品质有保障。
*/
(function $123pan() {
	// 严格模式，确保代码安全执行，不越界
	'use strict';
	// unsafeWindow 检测，适用于 Via 这类无 unsafeWindow 的浏览器
	if (typeof (unsafeWindow) === 'undefined') window.unsafeWindow = window;

	let temp = {
		defaults: {
			vip: true,
			svip: true,
			pvip: true,
			dads: true,
			name: "壹贰叁的上级明赋云",
			photo: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40' viewBox='0 0 40 40'%3E%3Cg fill-rule='evenodd' clip-rule='evenodd'%3E%3Cpath d='M33.26 18.651 23.098 8.97s-2.103-1.86-4.08.175L30.88 20.653s-1.896 1.376-3.245 2.401c-1.155.881-1.675.73-2.83-.394-2.79-2.707-8.619-8.357-8.619-8.357s-1.318-1.998-3.427-.27c-2.11 1.727-5.182 3.774-5.182 3.774l-5.149-4.991L16.093 1.64s4.354-4.939 10.35 2.319l11.639 11.288-4.822 3.403Z' fill='%23E80808'%3E%3C/path%3E%3Cpath d='m6.757 21.345 10.161 9.678s2.104 1.86 4.08-.175L9.134 19.34s1.9-1.377 3.245-2.402c1.155-.88 1.676-.727 2.83.394 2.787 2.707 8.616 8.36 8.616 8.36s1.318 1.999 3.427.271c2.11-1.724 5.183-3.775 5.183-3.775l5.148 4.994-13.666 11.175s-4.354 4.94-10.352-2.316L1.925 24.754l4.832-3.409Z' fill='%23131628'%3E%3C/path%3E%3C/g%3E%3C/svg%3E",
			mail: "",
			phone: "",
			id: "",
			level: 128,
			endtime: 253402185600,
			debug: false,
		},
		swalDefault: {
			position: 'center',
			heightAuto: false,
			scrollbarPadding: false,
			confirmButtonText: `确认`,
			denyButtonText: `拒绝`,
			cancelButtonText: `取消`
		},
	}

	/*
	防止代码因其他原因被执行多次
	代码出自 “Via 轻插件”，作者谷花泰
	*/
	let key = encodeURIComponent('123 云盘会员青春版:主代码');
	if (window[key]) return;
	window[key] = true;

	let mount = idontknow("123panYouthMember");
	let info = {
		author: GM_info.script?.author,
		name: GM_info.script?.name,
		version: (GM_info.script?.version || "1.0.6.2"),
		icon: (GM_info.script?.icon || ""),
		maction: GM_info.scriptaction,
		mversion: GM_info.version,
	};

	let base = {
		hooker: {
			async fetch(input, init = {}) {
				let url = typeof input === 'string' ? input : input?.url || '';
				url = new URL(url, location.origin);
				if (rules.some(rule => rule.match(url) && rule.condition() && rule.runat === "header")) {
					if (!init.headers) init.headers = {};

					let tempHeaders = {};
					if (init.headers instanceof Headers) {
						for (let [key, value] of init.headers.entries()) {
							tempHeaders[key] = value;
						}
					} else {
						tempHeaders = { ...init.headers };
					}

					init.headers = new Headers(base.hooker.universal("fetch", url, tempHeaders, "header"));
				}
				if (rules.some(rule => rule.match(url) && rule.condition() && rule.runat === "end")) {
					try {
						let response = await originalFetch.apply(this, arguments);
						let responseText = await response.text();

						let res = base.hooker.universal("fetch", url, responseText, "end");

						return new Response(res, {
							status: response.status,
							statusText: response.statusText,
							headers: response.headers
						});
					} catch (e) {
						return originalFetch.apply(this, arguments);
					}
				}
				if (rules.some(rule => rule.match(url) && rule.condition() && rule.runat === "start")) {
					try {
						let res = base.hooker.universal("fetch", url, null, "start");
						return new Response(res, {
							status: 200,
							statusText: "OK",
							headers: { 'Content-Type': 'plain/text' }
						});
					} catch (e) {
						return originalFetch.apply(this, arguments);
					}
				}
				return originalFetch.apply(this, arguments);
			},
			send(data) {
				let url = this[mount];
				if (this.headers) {
					for (let [name, value] of Object.entries(this.headers)) {
						originalSetRequestHeader.call(this, name, value);
					}
				}
				if (rules.find(rule => rule.match(url) && rule.condition() && rule.runat === "start")) {
					try {
						let res = base.hooker.universal("XHR", url, null, "start");
						Object.defineProperty(this, "responseText", {
							writable: true,
						});
						Object.defineProperty(this, "response", {
							writable: true,
						});
						this.response = res;
						this.responseText = res;
						['readystatechange', 'load', 'loadend'].forEach(prop => {
							this.dispatchEvent(new Event(prop));
							if (typeof this[prop] === 'function') this[prop]();
							if (typeof this[`on${prop}`] === 'function') this[`on${prop}`]();
						});
						return true;
					} catch (e) {
						return originalSend.apply(this, arguments);
					}
				}
				return originalSend.apply(this, arguments);
			},
			open(method, input) {
				let url = new URL(input, location.origin);
				this[mount] = url;
				this.addEventListener('readystatechange', function () {
					if (this.readyState === 4) {
						if (rules.some(rule => rule.match(url) && rule.condition() && rule.runat === "end")) {
							let res = base.hooker.universal("XHR", url, this.responseText, "end");
							Object.defineProperty(this, "responseText", {
								writable: true,
							});
							Object.defineProperty(this, "response", {
								writable: true,
							});
							this.response = res;
							this.responseText = res;
						}
					}
				});
				return originalOpen.apply(this, arguments);
			},
			setRequestHeader(name, value) {
				let url = this[mount];
				if (rules.some(rule => rule.match(url) && rule.condition() && rule.runat === "header")) {
					if (!this.headers) this.headers = {};
					this.headers[name] = value;
					this.headers = base.hooker.universal("XHR", url, { ...this.headers }, "header");
					return;
				}
				return originalSetRequestHeader.call(this, name, value);
			},
			atob(input) {
				// 尝试帮助进行 URL 解密
				try { return originalAtob(decodeURIComponent(input)); } catch { }
				return originalAtob(input);
			},
			universal(from, url, data, type) {
				let res, oriRes;
				try {
					res = typeof data === 'string' ? JSON.parse(data) : data;
					oriRes = typeof data === 'string' ? JSON.parse(data) : data;
				} catch (e) {
					res = data;
					oriRes = data;
				}
				let rule = rules.find(r => r.match(url) && r.condition() && r.runat === type);
				if (rule) {
					res = rule.action(res, url);
					try {
						if (res !== null && typeof res === 'object' && type === "header") {
							let tempHeaders = {};
							for (let key in res) {
								tempHeaders[key.toLowerCase().split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join('-')] = res[key];
							}
							res = tempHeaders;
						};
					} catch { }
					if (user.debug) {
						type === "start" && console.log(`【123 云盘会员青春版】Hook ${from} Virt`, "\n请求地址:", url.href, "\n模拟回复:", (res ? res : "空"));
						type === "end" && console.log(`【123 云盘会员青春版】Hook ${from} Repl`, "\n请求地址:", url.href, "\n原始回复:", (oriRes ? oriRes : "空"), "\n修改回复:", (res ? res : "空"));
						type === "header" && console.log(`【123 云盘会员青春版】Hook ${from} Head`, "\n请求地址:", url.href, "\n原始头部:", (oriRes ? oriRes : "空"), "\n修改头部:", (res ? res : "空"));
					}
					try {
						if (res !== null && typeof res === 'object' && type !== "header") res = JSON.stringify(res);
					} catch { }
				}
				return res;
			},
		},
		getSettings() {
			var now = GM_getValue('user_settings', {});
			return { ...temp.defaults, ...now };
		},
		async setSettings(settings) {
			await GM_setValue('user_settings', settings);
		},
		/**
		 * 显示脚本设置
		 * @author hmjz100
		 * @description 按时间倒序展示所有历史版本更新内容
		 */
		async showSettings() {
			let settings = base.getSettings();

			function generateHTML(title, key, type, other = {}) {
				function escapeHtml(str) {
					if (typeof str !== 'string') return str;
					return str.replace(/[&<>"']/g,
						tag => ({
							'&': '&amp;',
							'<': '<',
							'>': '>',
							'"': '&quot;',
							"'": '&#39;'
						}[tag] || tag)
					);
				}

				// 将 other 对象转换为属性字符串
				function buildAttrs(obj = {}) {
					let attrs = "";
					for (let [k, v] of Object.entries(obj)) {
						// 跳过空值或 undefined
						if (v === undefined || v === null) continue;
						if (typeof v === "boolean") {
							if (v) attrs += ` ${k}`;
							continue;
						}
						// 其他值作为 k="value"
						attrs += ` ${k}="${escapeHtml(String(v))}"`;
					}
					return attrs;
				};

				let control = "";
				let attrs = buildAttrs(other);

				if (type === "boolean") {
					let checked = settings[key] == true ? ' checked' : '';
					control = `<input class="swal2-input ym-input" type="checkbox" data-key="${key}"${checked}${attrs} />`
				} else if (type === "string") {
					let value = settings[key] || '';
					let inputType = other?.type ? `type="${escapeHtml(String(other.type))}"` : 'type="text"';
					control = `<input class="swal2-input ym-input" autocomplete="off" ${inputType} data-key="${key}" value="${escapeHtml(value)}"${attrs} />`
				}

				if (title.includes('|')) {
					const parts = title.split('|').map(part => `<span>${part}</span>`);
					title = parts.join('<br/>');
				} else {
					title = `<span>${title}</span>`;
				}

				return `<label class="ym-setting-item" data-key="${key}">
					<div>${title}</div>${control}
				</label>`;
			}

			let dialog = await Swal.fire({
				...temp.swalDefault,
				icon: 'info',
				iconHtml: '⚙︎',
				title: '设置',
				html: `<div class="ym-settings">
					<div class="block">
						<blockquote>
							<div>风雨送春归，飞雪迎春到。已是悬崖百丈冰，犹有花枝俏。</div>
							<div>俏也不争春，只把春来报。待到山花烂漫时，她在丛中笑。</div>
						</blockquote>
					</div>
					<div class="block">Tips: 设置即时存储，更改将在刷新后呈现</div>
					<div class="block">PS. 您还可以通过脚本管理器菜单打开此窗口</div>
					${generateHTML("会员|<small>显示为 VIP</small>", "vip", "boolean")}
					${generateHTML("超级会员|<small>显示为 SVIP</small>", "svip", "boolean")}
					${generateHTML("长期会员|<small>显示为长期 VIP</small>", "pvip", "boolean")}
					${generateHTML("隐藏广告|<small>仅 SVIP 有效</small>", "dads", "boolean")}
					${generateHTML("昵称|<small>对外展示信息</small>", "name", "string", { "type": "string", "placeholder": "留空将使用已登录账户信息" })}
					${generateHTML("头像|<small>对外展示信息</small>|<small>可以是 <code>http(s):</code> 或 <code>data:</code> 链接</small>", "photo", "string", { "type": "string", "placeholder": "留空将使用已登录账户信息" })}
					${generateHTML("邮箱|<small>对外展示信息</small>|<small>为确保安全 不建议修改</small>", "mail", "string", { "type": "string", "placeholder": "留空将使用已登录账户信息" })}
					${generateHTML("手机号|<small>对外展示信息</small>|<small>为确保安全 不建议修改</small>", "phone", "string", { "type": "number", "placeholder": "留空将使用已登录账户信息" })}
					${generateHTML("账户标识|<small>对外展示信息</small>|<small>为确保安全 不建议修改</small>", "id", "string", { "type": "number", "placeholder": "留空将使用已登录账户信息" })}
					${generateHTML("成长容量等级|<small>对外展示信息</small>|<small>最高可叠加 128 级</small>", "level", "string", { "type": "number", "min": "1", "max": "128", "placeholder": temp.defaults.level })}
					${generateHTML("会员过期时间|<small>对外展示信息</small>|<small>Unix 时间戳</small>", "endtime", "string", { "type": "number", "min": "-2147483648", "max": "253402185600", "placeholder": temp.defaults.endtime })}
					${generateHTML("显示调试信息到控制台", "debug", "boolean")}
				</div>
				<style>
					div:where(.swal2-container) div:where(.swal2-popup) {
						width: 35em;
						padding: 0 0 1.25em;
					}

					.ym-settings .ym-setting-item{display:flex;align-items:center;justify-content:space-between;margin-top:1em}
					.ym-settings .ym-setting-item > *:nth-child(1){text-align:left}
					.ym-settings .ym-setting-item > *:nth-child(2){max-width:80%;display:flex;justify-content:space-between;align-items:center}
					.ym-settings .ym-setting-item .ym-setting-item{margin:0;gap:5px}
					.ym-settings .ym-input{padding:8px 10px!important;border:1px solid #c2c2c2;border-radius:5px;font-size:14px!important;margin:0;appearance:auto!important}
					.ym-settings .ym-setting-item > .ym-input{width:50%}
					.ym-settings .ym-input[type=checkbox]{height:20px;width:20px;padding:0!important;background-image:none!important}

					.ym-settings .block { margin-bottom:20px }
					.ym-settings .block blockquote{
						padding:0.7em;
						border-left:5px solid #bdbdbd;
						background-color:#f9f9f9;
						margin:0;
					}
					@media (prefers-color-scheme:dark){
						/* 适配 LinkSwift 的自适应深色模式 */
						html:has(#swal-pub-dark-style) .ym-settings .block blockquote{
							border-left:5px solid #7A7C84;
							background-color:#464851;
						}
					}

					.ym-a{position:relative;vertical-align:baseline;color:#574AB8;border-bottom:2px solid #574AB8;text-decoration:none!important;transition:color.3s,opacity.3s;will-change:color,opacity;overflow:hidden}
					.ym-a::before{content:"";position:absolute;left:0;bottom:0;width:100%;height:100%;background-color:#574AB8;transform:scaleY(0);transform-origin:bottom center;transition:transform.15s,opacity.3s;will-change:transform;z-index:-1}
					.ym-a:hover,.ym-a:focus{color:#fff}
					.ym-a:hover::before,.ym-a:focus::before{transform:scaleY(1)}
					.ym-a:active{color:#fff;opacity:0.8}

					.swal2-footer,div:where(.swal2-container) div:where(.swal2-footer){flex-direction:column;justify-content:center;align-items:center}
					.swal2-footer p,div:where(.swal2-container) div:where(.swal2-footer) p{margin:0;padding:0}
				</style>`,
				allowOutsideClick: false,
				showCloseButton: true,
				showDenyButton: true,
				confirmButtonText: '刷新',
				denyButtonText: "关闭",
				didOpen: (toast) => {
					let element = $(toast);
					element.find(".ym-settings input").each((index, tag) => {
						$(tag).on("change", async (event) => {
							let element = $(event.target);
							let key = element.data("key");
							if (element.attr("type") === 'checkbox') {
								settings[key] = element.prop("checked");
							} else {
								let value = element.val() || "";
								if (element.attr("type") === 'number') value = parseInt(value) || "";
								settings[key] = value;
							}
							await base.setSettings(settings);
						})
					})
				},
				footer: `<p>用爱发电不接广，共创开源新环境</p><p>感谢您选择会员青春版，喜欢的话就来给个 <a href="https://github.com/hmjz100/123panYouthMember" target="_blank" class="ym-a pl-a">Star</a> 吧~</p>`,
			});

			if (dialog.isConfirmed) {
				location.reload(); // 刷新页面使设置生效
			}
		},
		/**
		 * 等待指定元素加载完成并执行回调
		 * @author hmjz100
		 * @description 监听 DOM 元素是否出现，若未出现则每隔一段时间重试，直到找到为止。
		 * 支持在 iframe 内部查找元素，适用于异步加载场景。
		 * @param {string} selectorElem - 要等待的目标元素选择器
		 * @param {Function} actionFunction - 找到元素后执行的回调函数，接收 jQuery 元素作为参数，返回 true 可以不再继续寻找
		 * @param {boolean} [bWaitOnce=false] - 是否只执行一次回调，默认为 false，如果不设置为 true 的话需要自行判断是否对元素进行操作
		 * @param {string} [iframeSelector] - 若目标元素位于 iframe 中，传入 iframe 的选择器
		 * @param {string} [controlKey] - 控制唯一性的键名，用于避免重复处理
		 */
		waitForKeyElements(selectorElem, actionFunction, bWaitOnce, iframeSelector, controlKey) {
			// 初始化管理器
			const manager = this.waitForKeyElements.manager || (
				this.waitForKeyElements.manager = {
					observers: new WeakMap(),
					tasks: new Map(),
					instanceCounter: 0
				}
			);
			const targetDoc = iframeSelector
				? $(iframeSelector).get(0)?.contentDocument
				: document;
			if (!targetDoc) return; // 无效文档直接返回
			// 生成唯一控制键
			controlKey = controlKey || `wkfe_${manager.instanceCounter++}`;
			// 清理重复任务
			const existingTask = manager.tasks.get(controlKey);
			if (existingTask) {
				existingTask.observer.disconnect();
				manager.tasks.delete(controlKey);
			}
			// 创建MutationObserver回调
			const processElements = () => {
				const elements = $(selectorElem, targetDoc);
				let foundActive = false;
				elements.each((i, el) => {
					const jEl = $(el);
					const isProcessed = jEl.data(controlKey);
					if (isProcessed) return true; // 跳过已处理元素
					const cancelAction = actionFunction(jEl);
					if (cancelAction) {
						foundActive = true;
					} else if (bWaitOnce) {
						jEl.data(controlKey, true); // 标记已处理
					}
				});
				// 一次性任务且找到有效元素时清理
				if (bWaitOnce && foundActive) {
					observer.disconnect();
					manager.tasks.delete(controlKey);
				}
			};
			// 创建Observer实例
			const observer = new MutationObserver(processElements);
			// 配置并启动观察
			observer.observe(targetDoc.documentElement, {
				childList: true,
				subtree: true,
				attributes: true,
				characterData: true
			});
			// 注册任务
			manager.tasks.set(controlKey, {
				observer,
				targetDoc
			});
			// 立即执行初始检查
			processElements();
		},
	}

	let user = base.getSettings();

	let rules = [
		{
			// 用户信息 基本
			runat: "end",
			match: (url) => url.pathname.includes('api/user/info'),
			condition: () => user.vip == true,
			action: (res, url) => {
				res.data.Vip = true // VIP
				res.data.VipLevel = res.data.UserVipDetail.VipCode = user.pvip ? 3 : user.svip ? 2 : 1

				if (user.name) res.data.Nickname = user.name;
				if (user.photo) res.data.HeadImage = user.photo;
				if (user.mail) res.data.Mail = user.mail;
				if (user.phone) res.data.Passport = Number(user.phone);
				if (user.id) res.data.UID = Number(user.id);
				if (user.level) res.data.GrowSpaceAddCount = Number(user.level);
				if (user.dads == true) res.data.IsShowAdvertisement = false;

				if (!user.endtime) user.endtime = 253402185600
				let vipEndTime = new Date(user.endtime * 1000);
				res.data.VipExpire = vipEndTime.toLocaleString();
				res.data.UserVipDetailInfos = res.data.UserVipDetail.UserVipDetailInfos = [
					{
						VipDesc: '青春创始包',
						TimeDesc: ` ${new Date(1638288000 * 1000).toLocaleDateString()} 生效`,
					},
					{
						VipDesc: '衣食所系包',
						TimeDesc: ` ${new Date(1727539200 * 1000).toLocaleDateString()} 生效`,
					},
					{
						VipDesc: user.svip == true ? "SVIP会员" : "VIP会员",
						TimeDesc: ` ${vipEndTime.toLocaleDateString()} 到期`,
						IsUse: vipEndTime >= new Date(),
						endTime: user.endtime,
						EndTime: user.endtime,
						StartTime: 1638288000
					}
				]
				if (user.pvip == true) {
					res.data.VipExpire = "永久有效";
					res.data.UserVipDetail.UserPermanentVIPDetailInfos = [{
						VipDesc: "长期VIP会员",
						TimeDesc: " 永久有效",
						IsUse: true
					}]
				}

				return res;
			}
		},
		{
			// 用户信息 基本
			runat: "end",
			match: (url) => url.pathname.includes('api/user/benefit'),
			condition: () => user.vip == true,
			action: (res, url) => {
				if (res.data?.benefitList) {
					res.data.benefitList = [
						...(user.pvip == true ? [{
							"benefitType": "长期VIP会员",
							"desc": "模拟",
							"startTime": new Date(1638288000 * 1000).toLocaleDateString(),
							"endTime": "",
							"sourceType": "123 云盘会员青春版",
							"isEffect": true
						}] : []),
						{
							"benefitType": user.svip == true ? "SVIP会员" : "VIP会员",
							"desc": "模拟",
							"startTime": new Date(1638288000 * 1000).toLocaleDateString(),
							"endTime": new Date(user.endtime * 1000).toLocaleDateString(),
							"sourceType": "123 云盘会员青春版",
							"isEffect": true
						},
						...res.data.benefitList
					]
				}
				return res;
			}
		},
		{
			// 用户信息 报告
			runat: "start",
			match: (url) => url.pathname.includes('user/report/info'),
			condition: () => user.vip == true,
			action: (res, url) => {
				res = {
					"code": 0,
					"message": "ok",
					"data": {
						"vipType": user.pvip ? 3 : user.svip ? 2 : 1,
						"vipSub": 0,
						"developSub": 0,
						"packType": [0]
					},
					"x-traceID": null
				};
				return res;
			}
		},
		{
			// 文件信息 下载
			runat: "end",
			match: (url) => ['file/download_info', 'file/batch_download_info', 'share/download/info', 'file/batch_download_share_info'].some(path => url.pathname.includes(path)),
			condition: () => true,
			action: (res, url) => {
				if (res.data && (res.data.DownloadUrl || res.data.DownloadURL)) {
					// 统一处理 DownloadUrl / DownloadURL
					let origKey = res.data.DownloadUrl ? 'DownloadUrl' : 'DownloadURL';
					let origURL = new URL(res.data[origKey]);
					let finalURL;
					if (origURL.origin.includes("web-pro")) {
						let params = ((url) => { try { return decodeURIComponent(atob(url)) } catch { return atob(url) } })(origURL.searchParams.get('params'));
						let directURL = new URL(params, origURL.origin);
						directURL.searchParams.set('auto_redirect', 0);
						origURL.searchParams.set('params', btoa(directURL.href));
						finalURL = decodeURIComponent(origURL.href);
					} else {
						origURL.searchParams.set('auto_redirect', 0);
						let newURL = new URL('https://web-pro2.123952.com/download-v2/ ', origURL.origin);
						newURL.searchParams.set('params', btoa(encodeURI(origURL.href)));
						newURL.searchParams.set('is_s3', 0);
						finalURL = decodeURIComponent(newURL.href);
					}
					res.data[origKey] = finalURL;
				}
				if (res?.code === 5113 || res?.code === 5114 || res?.message?.includes("下载流量已超出")) {
					if (url.pathname.includes("batch_download")) {
						res = {
							code: 400,
							message: "请不要多选文件！服务器不允许免费使用打包下载，已为您拦截支付窗口（123 云盘会员青春版）",
							data: null
						};
					} else {
						res = {
							code: 400,
							message: "您今日下载流量已超出限制。服务器不允许免费获取下载直链，已为您拦截支付窗口（123 云盘会员青春版）",
							data: null
						}
					}
				}
				return res;
			}
		},
		{
			// 文件信息 下载
			runat: "header",
			match: (url) => ['file/download_info', 'file/batch_download_info', 'share/download/info', 'file/batch_download_share_info'].some(path => url.pathname.includes(path)),
			condition: () => true,
			action: (res, url) => {
				res.platform = "android"; // 这是典型的安卓思维，安卓逻辑，安卓后端，安卓代码，安卓安卓……
				return res;
			}
		},
		{
			// 文件信息 视频
			runat: "end",
			match: (url) => url.pathname.includes("api/video/play/info"),
			condition: () => true,
			action: (res, url) => {
				// 移除没有播放链接的选项
				if (res.data?.video_play_info) res.data.video_play_info = res.data.video_play_info.filter(item => item.url !== "");
				return res;
			}
		},
		{
			// 手机验证码、邮箱验证码、忘记密码、修改密码、修改信息、账号注销、设备注销、创建支付、邮件验证
			runat: "start",
			match: (url) => ['user/get_vcode', 'get/mail_code', 'user/forget_pwd', 'user/modify_passport', '/user/whether/modify_passport', 'user/modify_info', 'identify/verify', 'user/log_off', 'user/kick_device', 'order/prepayment', 'option/mail'].some(path => url.pathname.includes(path)),
			condition: () => user.mail || user.phone || user.id,
			action: (res, url) => {
				let numbers = [5107, 5104, 5300, 6006, 6001, 100011, 5012];
				res = {
					"code": url.pathname.includes('order/prepayment') ? numbers[Math.floor(Math.random() * numbers.length)] : 400,
					"message": "为确保账户安全，修改任意一个关键数据（邮箱、手机号、ID）后，将禁止执行本操作（123 云盘会员青春版）",
					"data": null
				}
				return res;
			}
		},
		{
			// 汇报信息
			runat: "start",
			match: (url) => url.pathname.includes('transfer/metrics/whether/report'),
			condition: () => true,
			action: (res, url) => {
				res = {
					"code": 0,
					"message": "success",
					"data": {
						"dll_err_open": 1,
						"download_coroutine": 10,
						"duration": 300,
						"pc_report": 0,
						"status": false,
						"upload_coroutine": 5,
						"web_report": 0
					}
				}
				return res;
			}
		},
		{
			// 汇报信息 视频
			runat: "start",
			match: (url) => ['api/metrics/up', 'video/metrics/whether/report', 'api/video/metrics', 'restful/goapi/v1/content/payment/purchase-status'].some(path => url.pathname.includes(path)),
			condition: () => true,
			action: (res, url) => {
				res = {
					"code": 0,
					"message": "success",
					"data": {
						"status": true
					}
				}
				return res;
			}
		},
		{
			// 汇报信息 友盟数据派
			runat: "start",
			match: (url) => url.host.includes('shujupie') && url.pathname.includes('web_logs'),
			condition: () => true,
			action: (res, url) => {
				let json = { "install_campaign": "unknown", "install_channel": "unknown", "install_referer_domain": "unknown", "install_datetime": "unknown" }
				res = {
					"imprint": btoa(JSON.stringify(json))
				}
				return res;
			}
		},
		{
			// 汇报信息 阿里业务实时监控服务ARMS
			runat: "start",
			match: (url) => url.host.includes('arms-retcode') && url.pathname.includes('r.png'),
			condition: () => true,
			action: (res, url) => {
				res = "";
				return res;
			}
		},
	];

	/**
	 * 控制台输出
	 * @author 油小猴
	 * @author hmjz100
	 * @description 来自【网盘智能识别助手】，有改动
	 */
	console.log(`%c %c 123 云盘会员青春版\n虚拟 123 云盘会员，可超 1GB 上限下载\n仓库：https://github.com/hmjz100/123panYouthMember\n版本：${info.version}\n领域：${(window.self !== window.top ? "[iframe] " : "") + (document.title ? (document.title + " (" + location.origin + location.pathname + ")") : location.href)}`, `background:url(${info.icon}) center center no-repeat;background-size:12px;padding:3px`, `padding:2px`);

	var originalFetch = fetch;
	unsafeWindow.fetch = base.hooker.fetch;
	var originalSend = XMLHttpRequest.prototype.send;
	unsafeWindow.XMLHttpRequest.prototype.send = base.hooker.send;
	var originalOpen = XMLHttpRequest.prototype.open;
	unsafeWindow.XMLHttpRequest.prototype.open = base.hooker.open;
	var originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
	unsafeWindow.XMLHttpRequest.prototype.setRequestHeader = base.hooker.setRequestHeader;
	var originalAtob = atob;
	unsafeWindow.atob = base.hooker.atob;

	GM_registerMenuCommand('⚙️ 设置', () => {
		base.showSettings();
	});

	// 旧版 分享 提示
	base.waitForKeyElements(".contentBorder .cleanNet", (element) => {
		if (element.find("div").data("replaced")) return;
		let text = "用爱发电不接广，共创开源新环境；感谢您选择会员青春版，喜欢的话就来给个 Star 吧~ >>>";
		function replaceElement(target) {
			let div = $("<div></div>");
			div.data("replaced", true);

			let githubLink = $(`<span style="color:#574AB8">${text}</div>`);
			githubLink.off("click").on("click", (e) => {
				window.open("https://github.com/hmjz100/123panYouthMember", "_blank");
			});
			div.append(githubLink);
			div.append("<br/>");

			let settingsLink = $(`<span style="color:#574AB8">脚本设置 >>></div>`);
			settingsLink.off("click").on("click", (e) => {
				base.showSettings();
			});
			div.append(settingsLink);

			target.replaceWith(div);
		}
		replaceElement(element.find("span"));
		let observer = new MutationObserver((mutations) => {
			mutations.forEach((mutation) => {
				if (mutation.type === 'childList' || mutation.type === 'characterData') {
					if (element.find("> div").length === 0 || element.find("> span").text() !== text) {
						replaceElement(element.find("> span"));
					}
				}
			});
		});
		observer.observe(element[0], {
			childList: true,
			subtree: true,
			characterData: true
		});
		element.data("observer", observer);
	}, true)
	// 新版 主页 提示
	base.waitForKeyElements(".frontend-layout-header > .frontend-layout-header-left > .frontend-layout-header-left-cdn", (element) => {
		if (element.data("replaced")) return;
		element.children(':first').text("用爱发电不接广，共创开源新环境；感谢您选择会员青春版，喜欢的话就来给个 Star 吧~");
		element.data("replaced", true);

		let div = $(`<div style="display:flex;gap:7px"></div>`);

		let githubLink = element.find(".frontend-layout-header-left-cdn-button").clone();
		githubLink.contents().filter((_, n) => n.nodeType === 3 && $.trim(n.nodeValue)).first().get(0).nodeValue = 'Github';
		githubLink.off("click").on("click", function () {
			window.open("https://github.com/hmjz100/123panYouthMember", "_blank");
		});
		div.append(githubLink);

		let settingsLink = element.find(".frontend-layout-header-left-cdn-button").clone();
		settingsLink.contents().filter((_, n) => n.nodeType === 3 && $.trim(n.nodeValue)).first().get(0).nodeValue = '脚本设置';
		settingsLink.off("click").on("click", function () {
			base.showSettings();
		});
		div.append(settingsLink);

		element.find(".frontend-layout-header-left-cdn-button").replaceWith(div);
	}, true)
	// 新版 分享 提示
	base.waitForKeyElements(".FileList-CleanNet-cleanNet-container", (element) => {
		if (element.data("replaced")) return;
		element.children('span:first').text("用爱发电不接广，共创开源新环境；感谢您选择会员青春版，喜欢的话就来给个 Star 吧~");
		element.data("replaced", true);

		let div = $(`<div style="display:flex"></div>`);

		let githubLink = element.find(".FileList-CleanNet-link").clone();
		githubLink.find(".FileList-CleanNet-text").text('Github');
		githubLink.off("click").on("click", function () {
			window.open("https://github.com/hmjz100/123panYouthMember", "_blank");
		});
		div.append(githubLink);

		let settingsLink = element.find(".FileList-CleanNet-link").clone();
		settingsLink.find(".FileList-CleanNet-text").text('脚本设置');
		settingsLink.off("click").on("click", function () {
			base.showSettings();
		});
		div.append(settingsLink);

		element.find(".FileList-CleanNet-link").replaceWith(div);
	}, true)
	// 新版 分享 会员背景
	base.waitForKeyElements(".web-body.svip-body.backgroundImage", (element) => {
		let url = `url("https://statics.123pan.com/share-static/dist/login_img_bg_home.png")`;
		if (user.svip == true && element.css("background-image") === `url("")`) {
			element.css("background-image", url);
		}
	})
	// 这是啥？我不到啊
	function idontknow(input) {
		let charArray = input.split("");
		// 这是 Fisher-Yates 洗牌算法的实现
		for (let i = charArray.length - 1; i > 0; i--) {
			let j = Math.floor(Math.random() * (i + 1));
			[charArray[i], charArray[j]] = [charArray[j], charArray[i]];
		}
		return charArray.join("");
	}
})();